
#include<iostream>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<signal.h>
#include<cassert>
#include<string>
#include<cerrno>

//日志等级
#define NORMAL 0
#define DEBUG 1
#define ERROR 2 
//要打印的日志等级
#define ENABLE_LOG_LEVEL NORMAL  //假设我们只打印DEBUG及以上的等级


#define LOG(level,format,...) do{\
    if(level<ENABLE_LOG_LEVEL) break; /*过滤掉低等级的日志信息*/ \
    time_t t =time(NULL);\
    struct tm* ptm = localtime(&t);\
    assert(ptm);\
    char str[16]={0};\
    size_t ret = strftime(str,sizeof str -1,"%H:%M:%S",ptm);\
    assert(ret!=-1);\
    fprintf(stdout,"%s [%s:%d] " format"\n",str,__FILE__,__LINE__,##__VA_ARGS__);\
}while(0)

//普通日志
#define NORMAL_LOG(format,...) LOG(NORMAL,format,##__VA_ARGS__);

//调试日志
#define DEBUG_LOG(format,...) LOG(DEBUG,format,##__VA_ARGS__);

//错误日志
#define ERROR_LOG(format,...) LOG(ERROR,format,##__VA_ARGS__);



class Socket
{
private:
    int _sock;
public:
    Socket():_sock(-1){}
    Socket(int sock):_sock(sock){}
    ~Socket(){Close();}
    int Fd()const {return _sock;}
    void Create()                   //创建套接字
    {
        _sock = socket(AF_INET,SOCK_STREAM,0);
        if(_sock == -1)
        {
            ERROR_LOG("Create failed");
            abort();
        }
    }
    void Close()                    //关闭
    {
        close(_sock);
    }
    bool Bind(const std::string ip ,uint16_t port)const  //绑定地址信息
    {
        struct sockaddr_in addr;
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        int ret = bind(_sock,(struct sockaddr*)&addr,sizeof addr);
        if(ret==-1)
        {
            ERROR_LOG("Bind failed");
            abort();
        }
        return true; 
    }
    int Acceptor()const               //获取新连接
    {
        int newfd = accept(_sock,nullptr,nullptr);
        if(newfd<=0)
        {
            if(errno == EAGAIN ||errno ==EWOULDBLOCK ||errno == EINTR)  //前两个表示没有新连接，EINTR表示获取过程被信号打断
            return 0 ;
            ERROR_LOG("Acceptor failed");  //否则表示获取失败
            return -1;
        }
        return newfd;
    }
    bool Connect(std::string ip , uint16_t port)const      //建立连接
    {
        struct sockaddr_in addr;
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        int ret = connect(_sock,(struct sockaddr*)&addr,sizeof addr);
        if(ret < 0)     //连接出错
        {
            ERROR_LOG("Connect failed");
            return false;
        }
        return true;
    }
    ssize_t Send(const char*in,size_t len)const      //写入数据到内核发送缓冲区(非阻塞写入)
    {
        ssize_t ret = write(_sock,in,len);
        if(ret < 0)
        if(!(errno == EAGAIN ||errno == EWOULDBLOCK || errno ==EINTR))
        ERROR_LOG("Send failed");
        return ret;
    }
    ssize_t Read(char* out, size_t len)  //读取数据 (非阻塞读取)
    {
        ssize_t ret = read(_sock,out,len);
        if(ret < 0)
        { 
            if(!(errno == EAGAIN ||errno == EWOULDBLOCK || errno ==EINTR))
            ERROR_LOG("Read failed");
        }
        return ret;
    }
#define LISTEN_BACKLOG 1024
    bool Listen()               //开始监听
    { 
        int ret = listen(_sock,LISTEN_BACKLOG);
        if(ret < 0) 
        {
            ERROR_LOG("Listen failed");
            return false;
        } 
        return true;
    }
    void SetNonBlock()      //设置非阻塞    
    {
        int flag = fcntl(_sock,F_GETFD);
        fcntl(_sock,F_SETFD,flag | O_NONBLOCK | O_CLOEXEC);  //顺便设置不可被拷贝了
    }
    bool SetAddrReuse()         //设置地址复用
    {
        int val = 1;
        int ret = setsockopt(_sock,SOL_SOCKET,SO_REUSEADDR | SO_REUSEPORT ,&val,sizeof val);
        //设置 SO_REUSEADDR 可以绑定处于 TIME_WAIT 状态的端口 
        //设置 SO_REUSEPORT 可以让一个端口被多个 socket 绑定，可以用于实现负载均衡 
        if(ret == -1)
        {
            ERROR_LOG("SetAddrReuse failed");
            return false;
        }
        return true;
    }
#define DEFAULT_SERVER_IP "0.0.0.0"
    void CreatServerSocket(uint16_t port)  //只需要传端口号就行了
    {
        Create();
        bool ret = Bind(DEFAULT_SERVER_IP,port);
        if(!ret) 
        {
            ERROR_LOG("Bind failed");
            abort();
        }
        SetNonBlock();
        ret = SetAddrReuse();
        if(!ret) 
        {
            ERROR_LOG("SetAddrReuse failed");
            abort();
        }
        ret = Listen();
        if(!ret) 
        {
            ERROR_LOG("Listen failed");
            abort();
        } 
    }
    void CreatClientSocket(std::string ip,uint16_t port)
    {
        Create();
        SetNonBlock();
        Connect(ip,port);
    }
};

